package elements.axis ;
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.display.DisplayObject;
import flash.geom.Rectangle;
import elements.axis.AxisLabel;
import string.Utils;
import hxjson2.JSON;
import ColorConverter.Color;
// import DateUtils;

typedef XAXISLABELSSTRUCT = {
	var rotate		: Int;
	var visible		: Bool;
	var labels		: Array<String>;
	var text		: String;
	var steps		: Dynamic; // TODO - figure this out?
	var size		: Int;
	var align		: String;
	var color		: Color;
};

typedef LABELSTYLESTRUCT = {>XAXISLABELSSTRUCT,
	var x : String;
};

class XAxisLabels extends Sprite {
	
	public var need_labels:Bool;
	public var axis_labels:Array<String>;
	// JSON style:
		var style:XAXISLABELSSTRUCT; // TODO - define this motherfucking type
		var userSpecifiedVisible:Bool;
	
	//
	// Ugh, ugly code so we can rotate the text:
	//
	// [Embed(systemFont='Arial', fontName='spArial', mimeType='application/x-font', unicodeRange='U+0020-U+007E')]
	// [Embed(systemFont = 'Arial', fontName = 'spArial', mimeType = 'application/x-font')]
	
	public static var ArialFont__:Class<Dynamic>; // TODO - check if this is correct

	public function new ( json:Dynamic ) {
		super();
		this.need_labels = true;
		
		// TODO: remove this and the class
		// var style:XLabelStyle = new XLabelStyle( json.x_labels );
		
		this.style = {
			rotate:		0,
			visible:	false,
			labels:		null,
			text:		'#val#',  // default to display the position number or x value
			steps:		null,
			size:		10,
			align:		'auto',
			color:		Hex('#000000')
		};
		
		// cache the text for tooltips
		this.axis_labels = new Array();
		
		if( Reflect.hasField(json,"x_axis") && Reflect.hasField( json.x_axis, "labels") )
			ObjectHelper.merge_2( json.x_axis.labels, this.style );
			
		// save the user specified visible value foe use with autonclickabels
		this.userSpecifiedVisible = this.style.visible;
		// for user provided labels, default to visible if not specified
		if (this.style.visible == null) this.style.visible = true; 
		
		// Force rotation value if "rotate" is specified
		if (Std.is(this.style.rotate,String ))
		{
			if (this.style.rotate == "vertical")
			{
				this.style.rotate = cast(270, Int); // TODO - shit, recycled variables again, Int vs. String
			}
			else if (this.style.rotate == "diagonal")
			{
				this.style.rotate = -45;
			}
		}
		
		//this.style.color = Utils.get_color( this.style.color );
		
		if( Std.is( this.style.labels,Array ) && this.style.labels.length > 0)
		{
			//
			// we WERE passed labels
			//
			this.need_labels = false;
			
			var x: Float = 0;
			
			for ( s in this.style.labels )
			{
				var tmpStyle:Dynamic = {};
				ObjectHelper.merge_2( this.style, tmpStyle );
				tmpStyle.x = x;
				// we need the x position for #x_label# tooltips
				this.add( s, tmpStyle );
				x++;
			}
		}
	}
	
	//
	// we were not passed labels and need to make
	// them from the X Axis range
	//
	public function autonclickabel( range:Range, steps: Int ):Void {
		
		//
		// if the user has passed labels we don't do this
		//
		if ( this.need_labels ) {

			// Use the steps specific to labels if provided by user
			var lblSteps: Float = 1;
			if (this.style.steps != null) lblSteps = this.style.steps;

			// force max of 250 labels 
			if (Math.abs(range.count() / lblSteps) > 250) lblSteps = range.count() / 250;

			// guarantee lblSteps is the proper sign
			var rev:Bool = (range.min >= range.max); // min-max reversed?
			lblSteps = rev ? -Math.abs(lblSteps) : Math.abs(lblSteps);

			// Allow for only displaying some of the labels 
			var visibleSteps: Float = (Reflect.field(this.style,"visible-steps") == null) ? steps : Reflect.field(this.style,"visible-steps");

			var tempStyle:Dynamic = {};
			ObjectHelper.merge_2( this.style, tempStyle );
			var lblCount: Float = 0;
/*
			for ( i = range.min; rev ? i >= range.max : i <= range.max; i += lblSteps ) {
				tempStyle.x = i;
				// restore the user specified visble value
				if (this.userSpecifiedVisible == null)
				{
					tempStyle.visible = ((lblCount % visibleSteps) == 0);
					lblCount++;
				}
				else
				{
					tempStyle.visible = this.userSpecifiedVisible;
				}
				this.add( null, tempStyle );
			}
*/
			var i : Float = range.min;
			while (true) {
				if (rev)
				{
					if (i < range.max)
						break;
				}else
				{
					if (i > range.max)
						break;
				}
				tempStyle.x = i;
				// restore the user specified visble value
				if (this.userSpecifiedVisible == null)
				{
					tempStyle.visible = ((lblCount % visibleSteps) == 0);
					lblCount++;
				}
				else
				{
					tempStyle.visible = this.userSpecifiedVisible;
				}
				this.add( null, tempStyle );
				
				i += lblSteps;
			}

		}
	}
	
	public function add( label:Dynamic, style:Dynamic ) : Void
	{
		
		var label_style:LABELSTYLESTRUCT = {
			color:		style.color,
			text:		style.text,
			rotate:		style.rotate,
			size:		style.size,
			align:		style.align,
			visible:	style.visible,
			x:		style.x
		};

		//
		// inherit some properties from
		// our parents 'globals'
		//
		if(Std.is(label,String ))
			label_style.text = cast(label,String);
		else
			ObjectHelper.merge_2( label, label_style );

		// Replace magic date variables in x label text
		if (label_style.x != null) {
		//	label_style.text =  this.replace_magic_values(label_style.text, cast(label_style.x,Int));
		}
		
		// Map X location to label string
		this.axis_labels[cast(label_style.x,Int)] = label_style.text;

		// only create the label if necessary
		if (label_style.visible) {
			// our parent color is a number, but
			// we may have our own color:
			if(Std.is(label_style.color,String ))
				label_style.color = Utils.get_color( label_style.color );

			var l:TextField = this.make_label( label_style );
			
			this.addChild( l );
		}
	}
	
	public function get( i: Int ) : String
	{
		if( i<this.axis_labels.length )
			return this.axis_labels[i];
		return '';
	}

	
	public function make_label( label_style:Dynamic ):TextField {
		// we create the text in its own movie clip, so when
		// we rotate it, we can move the regestration point
		
		var title:AxisLabel = new AxisLabel();
		title.x = 0;
		title.y = 0;
		
		//this.css.parseCSS(this.style);
		//title.styleSheet = this.css;
		title.text = label_style.text;
		
		var fmt:TextFormat = new TextFormat();
		fmt.color = label_style.color;
	
		// TODO: != null
		if( label_style.rotate != 0 )
		{
			// so we can rotate the text
			fmt.font = "spArial";
			title.embedFonts = true;
		}
		else
		{
			fmt.font = "Verdana";
		}

		fmt.size = label_style.size;
		fmt.align = flash.text.TextFormatAlign.LEFT;
		title.setTextFormat(fmt);
		title.autoSize = flash.text.TextFieldAutoSize.LEFT;
		title.rotate_and_align( label_style.rotate, label_style.align, this );
		
		// we don't know the x & y locations yet...
		
		title.visible = label_style.visible;
		if (label_style.x != null)
		{
			// store the x value for use in resize
			title.xVal = label_style.x;
		}
		
		return title;
	}
	
	
	public function count() : Int
	{
		return this.axis_labels.length-1;
	}
	
	public function get_height() : Float
	{
		var height: Float = 0;
		for ( pos in 0...this.numChildren )
		{
			var child:DisplayDynamic = this.getChildAt(pos);
			height = Math.max( height, child.height );
		}
		
		return height;
	}
	
	public function resize( sc:ScreenCoords, yPos: Float ) : Void//, b:Box )
	{
		
		this.graphics.clear();
		var i: Float = 0;
		
		for ( pos in 0...this.numChildren )
		{
			var child:AxisLabel = cast(this.getChildAt(pos),AxisLabel);
			if (Math.isNaN(child.xVal))
			{
				child.x = sc.get_x_tick_pos(pos) + child.xAdj;
			}
			else
			{
				child.x = sc.get_x_from_val(child.xVal) + child.xAdj;
			}
			child.y = yPos + child.yAdj;
		}
	}
	
	//
	// to help Box calculate the correct width:
	//
	public function last_label_width() : Float
	{
		// is the last label shown?
	//			if( ( (this.labels.length-1) % style.step ) != 0 )
	//				return 0;
			
		// get the width of the right most label
		// because it may stick out past the end of the graph
		// and we don't want to truncate it.
	//			return this.mcs[(this.mcs.length-1)]._width;
		if ( this.numChildren > 0 )
			// this is a kludge compensating for ScreenCoords dividing the width by 2
			return AxisLabel(this.getChildAt(this.numChildren - 1)).rightOverhang * 2;
		else
			return 0;
	}
	
	// see above comments
	public function first_label_width() : Float
	{
		if( this.numChildren>0 )
			// this is a kludge compensating for ScreenCoords dividing the width by 2
			return AxisLabel(this.getChildAt(0)).leftOverhang * 2;
		else
			return 0;
	}
	
	public function die(): Void {
		
		this.axis_labels = null;
		this.style = null;
		this.graphics.clear();
		
		while ( this.numChildren > 0 )
			this.removeChildAt(0);
	}
	
	function replace_magic_values(labelText:String, xVal: Int):String {
		labelText = StringTools.replace(labelText,'#val#', NumberUtils.formatNumber(xVal));
		// labelText = DateUtils.replace_magic_values(labelText, xVal);
		return labelText;
	}

}
